<!--
 * @Description: 全局chart封装
 * @Author bianpengfei
 * @create 2022/9/16 09:54
 -->
<template>
  <div ref="chartWrapRef">
    <template v-if="_visible">
      <transition name="fade" appear>
        <div ref="chartRef" :style="_styles"></div>
      </transition>

      <!-- 收缩、展开-->
      <div v-if="_isBarChart && data.length > collapseLength" class="chart-isCollapse" @click="onCollapse">
        <template v-if="isCollapse"> <van-icon name="arrow-down" /> &nbsp;展开 </template>
        <template v-else> <van-icon name="arrow-up" />&nbsp;收缩 </template>
      </div>
    </template>

    <g-empty
      v-else
      :style="{
        width: '100%',
        height: height ? _styles.height : '3rem'
      }"
    ></g-empty>
  </div>
</template>

<script>
import { debounce, observerElementMutation, isFunction, isObject, capitalize } from '@bianpengfei/utils'

let mds
const mdsMap = (mds = require.context('./charts/', true, /\.js$/)).keys().reduce(
  (pre, cur) => ({
    ...pre,
    [cur
      .replace(/^\.\/(.*)\.\w+$/, '$1')
      .split('/')
      .at(-1)]: mds(cur).default
  }),
  {}
)

const TYPE_LIST = Object.keys(mdsMap)

let mdsMapHooks = Object.entries(mdsMap).reduce(
  (pre, [key, value]) => ({ ...pre, ...value, [`handler${capitalize(key)}`]: value?.handler }),
  {}
)

console.log('mdsMapHooks', TYPE_LIST, mdsMapHooks)

export default {
  name: 'GChart',
  props: {
    /**
     * 图表高度
     */
    height: {
      type: [Number, String],
      default: `300px`
    },

    /**
     * 图表类型
     */
    type: {
      type: String,
      default: 'pie',
      validator: (v) => TYPE_LIST.includes(v)
    },

    /**
     * 值
     */
    data: {
      type: Array,
      default: () => [] // [ { name: '药品收入',, valuey1: '',  valuez1: '', valuez1: '', valuez2: '', },]
    },

    /**
     * 颜色集合
     */
    seriesColors: {
      type: Array,
      default: () => [
        '#9E87FF',
        '#FDD56A',
        '#FDB36A',
        '#FD866A',
        '#1890FF',
        '#13C2C2',
        '#2FC25B',
        '#FACC14',
        '#F04864',
        '#8543E0',
        '#3436C7',
        '#223273'
      ]
    },

    /**
     * 类别名称集合
     */
    seriesNames: {
      type: Array,
      default: () => []
    },

    /**
     * 单位集合
     */
    seriesUnits: {
      type: Array,
      default: () => []
    },

    /**
     * 最高点和最低点背景颜色 颜色集合
     */
    seriesMarkPointColors: {
      type: Array,
      default: () => ['#7fffd4', '#ffc0cb'] // max min
    },

    /**
     * markLine 颜色集合
     */
    seriesMarkLineColors: {
      type: Array,
      default: () => [] // max min
    },

    /**
     * 增强chartOptions
     */
    enhanceChartOptions: {
      type: Function,
      default: null
    },

    /**
     * echarts 选项,
     */
    options: {
      type: Object,
      default: () => {}
    },

    /**
     * 当图表处于 bar时，折叠的长度默认是5
     */
    collapseLength: {
      type: Number,
      default: 5
    }
  },

  data() {
    return {
      chartOptions: {},
      isCollapse: true
    }
  },

  computed: {
    // 是bar类型的图表
    _isBarChart() {
      return this.type.startsWith('bar')
    },

    _visible() {
      return !!this.data.length
    },

    _styles() {
      const unit = String(this.height).replace(/\d+\.?/g, '') || 'px'
      let height = /^calc/.test(this.height) ? this.height : parseFloat(this.height) + unit
      if (this._isBarChart && !this.isCollapse) {
        height = Math.max(+this.height || 0, this._dataSource.length * 38) + 'px'
      }
      return {
        width: '100%',
        padding: '0px',
        height
      }
    },

    _dataSource() {
      if (this.type.startsWith('bar')) {
        return this.isCollapse ? this.data.slice(0, this.collapseLength) : this.data.slice()
      }
      return this.data
    },

    _dataObj() {
      const map = (key) => this._dataSource.filter((v) => Object.hasOwn(v, key)).map((v) => v[key])
      const keys = ['name', 'valuey1', 'valuey2', 'valuey3', 'valuez1', 'valuez2', 'valuez3']
      return keys.reduce((pre, cur) => ({ ...pre, [cur]: map(cur) }), {})
    },

    /**
     * 增强版
     * @returns {{unit: *, color: *, data: *, index: *, key: *}}
     * @private
     */
    _enhancerDataObj() {
      return ['valuey1', 'valuey2', 'valuey3', 'valuez1', 'valuez2', 'valuez3']
        .filter((v) => this._dataObj[v].length)
        .map((v, vi) => ({
          key: v,
          index: vi,
          name: this.seriesNames[vi] || '',
          color: this.seriesColors[vi] || '',
          markineColor: this.seriesMarkLineColors[vi] || this.seriesColors[vi] || '',
          unit: this.seriesUnits[vi] || '',
          data: this._dataObj[v]
        }))
        .reduce((pre, cur) => ({ ...pre, [cur.key]: cur }), { name: this._dataObj['name'] })
    }
  },

  created() {
    this._chart = undefined // echart 示例对象
    this._chartWidth = undefined // echart 逻辑像素 宽
    this._chartHeight = undefined // echart 逻辑像素 高

    this.$watch(
      () => this._dataSource,
      (nVal) => {
        this.changeOptions(nVal)
      },
      { deep: true }
    )

    this.$watch(
      () => this.enhanceChartOptions,
      (nVal) => {
        if (this._chart) {
          const hook = `handler${capitalize(this.type)}`
          const option = this?.[hook]?.call?.(this, this._dataSource) || {}
          this.chartOptions = nVal(option, this)
        }
      }
    )

    this.$watch(
      () => this.chartOptions,
      (nVal) => {
        this._chart && this._chart.setOption(nVal)
      }
    )
  },

  activated() {},

  mounted() {
    const onDebounceResize = debounce(this.onResize.bind(this), 300)
    window.addEventListener('resize', onDebounceResize, false)

    this._observerCb = () => {
      this._chartWidth = this.$refs?.chartWrapRef?.clientWidth || 0
      this._chartHeight = this.$refs?.chartWrapRef?.clientHeight || 0
    }

    this.changeOptions(this._dataSource)

    this._observerMuation ||= observerElementMutation({
      callback: () => this._observerCb(),
      options: { el: this.$refs.chartWrapRef, attribute: ['height', 'width'] }
    })
    this._observerMuation.start()

    this.$once('hook:beforeDestroy', () => {
      this._observerMuation.end()
      this._observerMuation = null
      if (this._chart) {
        this._chart.clear()
        this._chart.dispose()
        this._chart = null
        this.$refs.chartRef = null
      }
    })
  },

  methods: {
    ...mdsMapHooks,

    onCollapse() {
      this.isCollapse = !this.isCollapse
      this.$nextTick(() => {
        this.onResize()
      })
    },

    /**
     * 浏览器尺寸发生变化
     */
    onResize() {
      this._chart && this._chart.resize()
    },

    /**
     * 强制刷新
     * @expose 暴露给外部使用
     */
    forceUpdate() {
      this.chartOptions(this._dataSource)
    },

    /**
     * 改变echarts 配置项
     * @param data
     */
    changeOptions(data = []) {
      if (!this._visible) return (this._chart = null)
      this._observerCb()
      const hook = `handler${capitalize(this.type)}`
      let option = this?.[hook]?.call?.(this, data) || {}
      // console.log('optiosn', option)
      this.$nextTick(() => {
        this._chart ||= this.$echarts.init(this.$refs.chartRef)
        if (isFunction(this.enhanceChartOptions)) {
          option = { ...option, ...this.options }
          const chartOptions = this.enhanceChartOptions(option, this)
          if (!isObject(chartOptions)) throw Error('【enhanceChartOptions】方法返回值必须是一个普通的对象')
          this.chartOptions = chartOptions
        } else
          this.chartOptions = {
            ...option,
            ...this.options
          }
      })
    }
  }
}
</script>

<style scoped lang="scss">
.chart-isCollapse {
  display: flex;
  cursor: pointer;
  align-items: center;
  justify-content: center;
  height: 0.72rem;
  font-size: 0.26rem;
}
</style>
